package com.start.pay.wx;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult;
import com.github.binarywang.wxpay.bean.request.WxPayRefundRequest;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryResult;
import com.github.binarywang.wxpay.bean.result.WxPayRefundQueryResult;
import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.constant.WxPayConstants;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
import com.github.wxpay.sdk.WXPayUtil;
import com.start.common.tools.UUIdTools;
import com.start.pay.core.entity.StartPayOrderEntity;
import com.start.pay.core.entity.StartPlatformPlayConfig;
import com.start.pay.core.enums.StartPayState;
import com.start.pay.core.interfaces.StartBasePayPlatform;
import com.start.pay.core.model.PlatformOrderPayResult;
import com.start.pay.core.model.PlatformOrderResult;
import com.start.pay.core.model.PlatformResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import java.util.*;

@Slf4j
@Component
public class StartPlayPlatformWeiXin implements StartBasePayPlatform<StartPlatformPlayConfig, WxPayService> {
    private static final Map<String, WxPayService> serviceMap = new HashMap<>();
    private static final Map<String, StartPlatformPlayConfig> platformPlayConfigMap = new HashMap<>();

    @Override
    public String getPlatformType() {
        return "weiXinPay";
    }

    @Override
    public String getPlatformTypeLabel() {
        return "微信支付";
    }

    @Override
    public boolean hasAppId(String appId) {
        return serviceMap.containsKey(appId);
    }

    private WxPayService createWxOpenService(StartPlatformPlayConfig entity) {
        WxPayService wxPayService = new WxPayServiceImpl();
        wxPayService.setConfig(createWxPlayConfig(entity));
        return wxPayService;
    }

    private WxPayConfig createWxPlayConfig(StartPlatformPlayConfig entity) {
        WxPayConfig payConfig = new WxPayConfig();
        payConfig.setAppId(StringUtils.trimToNull(entity.getAppId()));
        payConfig.setMchId(StringUtils.trimToNull(entity.getMchId()));
        payConfig.setMchKey(StringUtils.trimToNull(entity.getMchKey()));
        payConfig.setSubAppId(StringUtils.trimToNull(entity.getSubAppId()));
        payConfig.setSubMchId(StringUtils.trimToNull(entity.getSubMchId()));
        payConfig.setNotifyUrl(StringUtils.trimToNull(entity.getNotifyUrl()));
        String certificate = StringUtils.trimToNull(entity.getCertificate());
        if (StringUtils.isEmpty(certificate)) return payConfig;
        JSONArray array = JSONArray.parseArray(certificate);
        if (array.size() < 1) return payConfig;
        JSONObject jsonObject = array.getJSONObject(0);
        String value = jsonObject.getString("value");
        payConfig.setKeyPath(value);
        return payConfig;
    }

    @Override
    public boolean addPlayConfig(StartPlatformPlayConfig config) {
        if (serviceMap.containsKey(config.getAppId())) {
            return true;
        }
        WxPayService wxMaService = createWxOpenService(config);
        serviceMap.put(config.getAppId(), wxMaService);
        platformPlayConfigMap.put(config.getAppId(), config);
        return true;
    }

    @Override
    public boolean removeByAppId(String appId) {
        if (!serviceMap.containsKey(appId)) {
            return true;
        }
        serviceMap.remove(appId);
        platformPlayConfigMap.remove(appId);
        return true;
    }

    /**
     * 获取平台服务
     *
     * @param appId
     * @return
     */
    @Override
    public WxPayService getPlatformServiceByAppId(String appId) {
        if (!serviceMap.containsKey(appId)) {
            return null;
        }
        return serviceMap.get(appId);

    }

    @Override
    public boolean updateByAppId(StartPlatformPlayConfig config) {
        if (!serviceMap.containsKey(config.getAppId())) {
            return false;
        }
        removeByAppId(config.getAppId());
        return addPlayConfig(config);
    }

    @Override
    public List<StartPlatformPlayConfig> allConfig() {
        if (platformPlayConfigMap.isEmpty()) {
            return new ArrayList<>();
        }
        return new ArrayList<>(platformPlayConfigMap.values());
    }

    @Override
    public Class<StartPlatformPlayConfig> getConfigClazz() {
        return StartPlatformPlayConfig.class;
    }

    @Override
    public PlatformResult createPay(StartPayOrderEntity entity) {
        if (!hasAppId(entity.getAppId())) {
            log.error("未找到AppId为:" + entity.getAppId() + "的支付配置");
            throw new RuntimeException("未找到AppId为:" + entity.getAppId() + "的支付配置");
        }
        StartPlatformPlayConfig config = platformPlayConfigMap.get(entity.getAppId());
        WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
        orderRequest.setAppid(entity.getAppId());
        orderRequest.setOpenid(entity.getUserOpenId());
        orderRequest.setMchId(config.getMchId());
        orderRequest.setNonceStr(WXPayUtil.generateNonceStr());
        orderRequest.setNotifyUrl(config.getNotifyUrl());
        orderRequest.setBody(entity.getGoodsName());
        orderRequest.setOutTradeNo(entity.getOrderNo());
        orderRequest.setTotalFee(entity.getTotalFee());
        orderRequest.setTradeType(entity.getTradeType());
        orderRequest.setSpbillCreateIp(entity.getClientIp());
        if (StringUtils.isAllBlank(config.getSignType())) {
            orderRequest.setSignType(WxPayConstants.SignType.MD5);
        } else {
            orderRequest.setSignType(config.getSignType());
        }

        PlatformResult result = new PlatformOrderResult();
        try {
            Object order = getPlatformServiceByAppId(entity.getAppId()).createOrder(orderRequest);
            result.setSuccess(true);
            result.setResultData(order);
            result.setPayState(StartPayState.PAY_ING);
        } catch (WxPayException e) {
            e.printStackTrace();
            result.setSuccess(false);
            result.setErrorReason(e.getMessage());
            result.setPayState(StartPayState.PAY_FAIL);
        }
        return result;
    }

    /**
     * 支付回调处理
     *
     * @param appId
     * @param platformCallData
     * @return
     */
    @Override
    public PlatformOrderResult payCallBack(String appId, String platformCallData) {
        WxPayService service = getPlatformServiceByAppId(appId);
        PlatformOrderResult result = new PlatformOrderResult();
        result.setResultData(true);
        try {
            WxPayOrderNotifyResult notifyResult = service.parseOrderNotifyResult(platformCallData);
            if (notifyResult.getReturnCode().equalsIgnoreCase("SUCCESS")) {
                result.setThirdPlatformOrderNo(notifyResult.getTransactionId());
                result.setOrderNo(notifyResult.getOutTradeNo());
                result.setSuccess(true);
            } else {
                result.setSuccess(false);
                result.setOrderNo(notifyResult.getOutTradeNo());
                result.setErrorReason(notifyResult.getErrCode() + "\n" + notifyResult.getErrCodeDes());
            }
        } catch (WxPayException e) {
            e.printStackTrace();
            result.setSuccess(false);
            result.setErrorReason(e.getMessage());
        }
        return result;

    }

    /**
     * 退款订单
     *
     * @param entity
     * @return
     */
    @Override
    public PlatformResult refundOrder(StartPayOrderEntity entity) {
        final WxPayService service = getPlatformServiceByAppId(entity.getAppId());
        StartPlatformPlayConfig config = platformPlayConfigMap.get(entity.getAppId());
        WxPayRefundRequest request = new WxPayRefundRequest();
        request.setAppid(config.getAppId());
        request.setMchId(service.getConfig().getMchId());
        request.setNonceStr(WXPayUtil.generateNonceStr());
        request.setOutTradeNo(entity.getOrderNo());
        request.setOutRefundNo(UUIdTools.createSnowFakeId());
        request.setTotalFee(entity.getTotalFee());
        request.setRefundFee(entity.getTotalFee());
        request.setNotifyUrl(config.getNotifyRefundUrl());
        request.setSignType(WxPayConstants.SignType.MD5);
        PlatformResult result = new PlatformOrderResult();
        try {
            result.setSuccess(true);
            result.setResultData(service.refund(request));

        } catch (WxPayException e) {
            e.printStackTrace();
            result.setSuccess(false);
            result.setErrorReason(e.getMessage());
        }
        return result;
    }

    /**
     * 退款回调
     *
     * @param appId
     * @param platformCallData
     * @return
     */
    @Override
    public PlatformOrderResult refundCallBack(String appId, String platformCallData) {
        final WxPayService service = getPlatformServiceByAppId(appId);
        PlatformOrderResult result = new PlatformOrderResult();
        try {
            final WxPayRefundNotifyResult notifyResult = service.parseRefundNotifyResult(platformCallData);
            if (notifyResult.getReturnCode().equalsIgnoreCase("SUCCESS")) {
                result.setSuccess(true);
                WxPayRefundNotifyResult.ReqInfo reqInfo = notifyResult.getReqInfo();
                result.setOrderNo(reqInfo.getOutTradeNo());
            } else {
                result.setSuccess(false);
                result.setErrorReason(notifyResult.getErrCode() + "\n" + notifyResult.getErrCodeDes());
            }
        } catch (WxPayException e) {
            e.printStackTrace();
            result.setSuccess(false);
            result.setErrorReason(e.getMessage());
        }
        return result;
    }

    /**
     * 查询订单
     *
     * @param orderEntity
     * @return
     */
    @Override
    public PlatformOrderPayResult queryPayOrder(StartPayOrderEntity orderEntity) {
        final WxPayService service = getPlatformServiceByAppId(orderEntity.getAppId());
        PlatformOrderPayResult result = new PlatformOrderPayResult();
        WxPayOrderQueryResult wxPayOrderQueryResult = null;
        try {
            String orderNo = StringUtils.isNotEmpty(orderEntity.getThirdPlatformOrderNo()) && StringUtils.isNotEmpty(orderEntity.getOrderNo()) ? "" : orderEntity.getThirdPlatformOrderNo();
            wxPayOrderQueryResult = service.queryOrder(orderNo, orderEntity.getOrderNo());
        } catch (WxPayException e) {
            e.printStackTrace();
            result.setQueryState(false);
            return result;
        }
        if (!wxPayOrderQueryResult.getReturnCode().equalsIgnoreCase("SUCCESS")) {
            result.setQueryState(false);
            return result;
        }
        result.setQueryState(true);
        switch (wxPayOrderQueryResult.getTradeState().toUpperCase()) {
            case "SUCCESS":
                result.setPayState(StartPayState.PAY_SUCCESS.getValue());
                break;
            case "REFUND":
                result.setPayState(StartPayState.PAY_REFUND_ING.getValue());
                break;
            case "CLOSED":
                result.setPayState(StartPayState.PAY_CANCEL.getValue());
                break;
            case "USERPAYING":
                result.setPayState(StartPayState.PAY_ING.getValue());
                break;
            case "PAYERROR":
                result.setPayState(StartPayState.PAY_EXCEPTION.getValue());
                break;
            case "NOTPAY":
                result.setPayState(StartPayState.UN_PAY.getValue());
                break;
        }
        return result;
    }

    @Override
    public PlatformOrderPayResult queryRefundOrder(StartPayOrderEntity orderEntity) {
        final WxPayService service = getPlatformServiceByAppId(orderEntity.getAppId());
        PlatformOrderPayResult result = new PlatformOrderPayResult();
        WxPayRefundQueryResult wxPayRefundQueryResult = null;
        try {
            wxPayRefundQueryResult = service.refundQuery(null, orderEntity.getOrderNo(), null, null);
        } catch (WxPayException e) {
            e.printStackTrace();
            result.setQueryState(false);
            return result;
        }
        if (!wxPayRefundQueryResult.getReturnCode().equalsIgnoreCase("SUCCESS")) {
            result.setQueryState(false);
            return result;
        }
        result.setQueryState(true);
        List<WxPayRefundQueryResult.RefundRecord> refundRecords = wxPayRefundQueryResult.getRefundRecords();
        Optional<WxPayRefundQueryResult.RefundRecord> success = refundRecords.stream().filter(e -> e.getOutRefundNo().equalsIgnoreCase(orderEntity.getOrderNo()) && e.getRefundStatus().equalsIgnoreCase("SUCCESS")).findAny();
        if (success.isPresent()) {
            WxPayRefundQueryResult.RefundRecord refundRecord = success.get();
            result.setPayState(-3);
        }
        return result;
    }

}
